home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / basic / qbcmp.zip / C1.BAS next >
BASIC Source File  |  1992-08-06  |  11KB  |  367 lines

  1. 'Experimental LZW Compressor for QuickBASIC 4.5
  2. 'By Rich Geldreich 1992
  3. 'This program is in the public domain: use as you wish!
  4. '(QB4.5 users: Use search & replace to change all of the "SSEG" strings
  5. 'to "VARSEG" strings in this program.)
  6. 'Please see QBLZW.BAS for more information on LZW compression in QB.
  7.  
  8. 'If you have and questions or problems, write/call:
  9. '
  10. 'Rich Geldreich
  11. '410 Market St.
  12. 'Gloucester City, NJ 08030
  13. '(609)-742-8752
  14. '
  15. 'Benchmarks:      ORIGINAL  HUFFMAN2.BAS  C1.BAS  ZIP
  16. 'BCL71ENR.LIB     263245    216495        191799  159324
  17. 'BIG_1_3.PCX      7401      3926          2735    2374
  18. 'MESSAGES.TXT     226989    151750        113077  84044
  19. 'TIME.MOD         155394    102447        87460   75101
  20. '
  21. '
  22. '
  23. ' Do not press ctrl+break while this program is compressing! The string
  24. ' pointers may change, which may result in an error!
  25.  
  26. DEFINT A-Z
  27. DECLARE SUB PutByte (A)
  28. DECLARE SUB PutCode (A)
  29. DECLARE SUB Rebuild.Table (New.Entries)
  30. DECLARE FUNCTION GetByte ()
  31. DECLARE SUB Hash (Prefix, Suffix, Index, Found)
  32.  
  33. CONST True = -1, False = 0
  34.  
  35. DIM SHARED Prefix(6576), Suffix(6576), Code(6576)
  36. DIM SHARED Used(4096)
  37.  
  38. DIM SHARED InBuffer$, IAddress, IEndAddress, Iseg
  39. DIM SHARED OutBuffer$, OStartAddress, OAddress, OEndAddress, Oseg
  40.  
  41. DIM SHARED CodeSize, CurrentBit, Char&
  42. DIM SHARED Shift(12) AS LONG
  43.  
  44.  
  45. FOR A = 0 TO 12: READ Shift(A): NEXT
  46. DATA 1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192
  47.  
  48.  
  49. LOCATE , , 1
  50. IF POS(0) <> 1 THEN PRINT
  51.  
  52.  
  53. InBuffer$ = STRING$(4000, 0)   'input buffer
  54. OutBuffer$ = STRING$(4000, 0)  'output buffer
  55.  
  56.  
  57. A& = SADD(OutBuffer$)
  58. A& = A& - 65536 * (A& < 0)
  59. Oseg = SSEG(OutBuffer$) + (A& \ 16)     'Segment of buffer
  60. OAddress = (A& MOD 16)                  'Current address in disk buffer
  61. OEndAddress = OAddress + 4000           'End address of  buffer
  62. OStartAddress = OAddress                'Start of buffer
  63.  
  64. 'Open input file
  65. File$ = COMMAND$
  66. IF File$ = "" THEN LINE INPUT "File to compress? "; File$: File$ = LTRIM$(RTRIM$(File$))
  67. IF File$ = "" THEN END
  68. OPEN File$ FOR BINARY AS #1
  69. FileLength& = LOF(1)
  70. 'Is it there?
  71. IF FileLength& = 0 THEN
  72.     CLOSE #1
  73.     KILL COMMAND$
  74.     PRINT COMMAND$; " not found"
  75.     END
  76. END IF
  77. 'Open output file
  78. OPEN "output.lzw" FOR BINARY AS #2
  79. 'Is it already there?
  80. IF LOF(2) <> 0 THEN
  81.     'Kill output file and reopen it
  82.     CLOSE #2
  83.     KILL "output.lzw"
  84.     OPEN "output.lzw" FOR BINARY AS #2
  85. END IF
  86. 'CurrentLoc& - position in input file
  87. CurrentLoc& = 2
  88.  
  89. 'Compression codes:
  90. 'Code 256 = end of file
  91. 'Code 257 = increase code size
  92. 'Code 258 = rebuild table
  93. 'Code 259 - 4095 = available for strings
  94. StartCode = 259                 'First LZW code that is available
  95. NextCode = 259
  96. 'The maximum code that can be represented in 9 bits
  97. MaxCode = 512
  98. 'Start with 9 bit code size
  99. CodeSize = 9
  100. 'Current bit position in Char& - use for PutCode
  101. CurrentBit = 0
  102. 'Char& is a temporary buffer; accumulates codes from main program and
  103. 'puts them in the output file once complete bytes have been
  104. 'built
  105. Char& = 0
  106.  
  107. GOSUB ClearTable
  108. 'Get first byte from file(it's a special case)
  109. Prefix = GetByte
  110.  
  111. PRINT "LZW Compressor For QuickBASIC 4.5"
  112. PRINT "By Richard Geldreich June 2nd, 1992"
  113. PRINT "Compressing "; File$
  114. PRINT : PRINT : PRINT
  115. 'First line to start updating statistics
  116. Y = CSRLIN - 3
  117. 'Main compression loop
  118. DO
  119.     DO
  120.      
  121.         IF CurrentLoc& > FileLength& THEN
  122.             PutCode Prefix
  123.             PutCode 256
  124.             PutCode 0: PutCode 0
  125.             OutBuffer$ = LEFT$(OutBuffer$, OAddress - OStartAddress)
  126.             LOCATE Y, 1
  127.             PRINT "Bytes In:"; CurrentLoc&; (100& * CurrentLoc&) \ FileLength&; "%"
  128.             BytesOut& = LOF(2) + (OAddress - OStartAddress)
  129.             PRINT "Bytes Out:"; BytesOut&
  130.             PRINT "Total Compression:"; 100 - ((100& * BytesOut&) \ CurrentLoc&); "%                         ";
  131.             PUT #2, , OutBuffer$
  132.             CLOSE
  133.             END
  134.         ELSE
  135.             
  136.             Suffix = GetByte
  137.             CurrentLoc& = CurrentLoc& + 1
  138.             'We now have a Prefix:Suffix to search for.
  139.             'If the search fails, put the Prefix in the output file
  140.             'and set the Prefix equal to the character which caused
  141.             'the failure.
  142.  
  143.             Hash Prefix, Suffix, Index, Found
  144.             IF Found = True THEN
  145.                 Prefix = Code(Index)
  146.                 'update how many times this string was used
  147.                 Used(Prefix) = Used(Prefix) + 1
  148.             END IF
  149.         END IF
  150.     LOOP WHILE Found = True
  151.  
  152.     'only increase the code size when required
  153.     DO WHILE Prefix >= MaxCode AND CodeSize < 12
  154.         PutCode 257
  155.         MaxCode = MaxCode * 2
  156.         CodeSize = CodeSize + 1
  157.     LOOP
  158.    
  159.     PutCode Prefix
  160.  
  161.     'Put the new string into the hash table.
  162.     Prefix(Index) = Prefix
  163.     Suffix(Index) = Suffix
  164.     Code(Index) = NextCode  'remember this string's code
  165.  
  166.     'Prefix is now equal to the character that caused the failure now.
  167.     Prefix = Suffix
  168.  
  169.     NextCode = NextCode + 1
  170.     'if there are too many strings then rebuild the encoding table
  171.     IF NextCode > 4096 THEN
  172.            
  173.         PutCode 258 'send rebuild table code to decompressor
  174.  
  175.         Rebuild.Table New.Entries
  176.         NextCode = New.Entries + StartCode
  177.        
  178.         IF NextCode > 4096 THEN
  179.             GOSUB ClearTable
  180.             NextCode = StartCode        'reset NextCode to top of tree
  181.         END IF
  182.  
  183.         CodeSize = 9
  184.         MaxCode = 512
  185.  
  186.         
  187.     END IF
  188.  
  189.     'let the impatient user know we haven't hung up (yet!)
  190.     PrintCounter = PrintCounter + 1     'see if time to update the
  191.     IF PrintCounter = 512 THEN          'screen
  192.         LOCATE Y, 1
  193.         PRINT "Bytes In:"; CurrentLoc&; (100& * CurrentLoc&) \ FileLength&; "%"
  194.         BytesOut& = LOF(2) + (OAddress - OStartAddress)
  195.         PRINT "Bytes Out:"; BytesOut&
  196.         PRINT "Compression:"; 100 - ((100& * BytesOut&) \ CurrentLoc&); "%  "; "CodeSize:"; CodeSize; "NextCode:"; NextCode; "   ";
  197.         PrintCounter = 0
  198.     END IF
  199. LOOP
  200. 'clears the hash table
  201. ClearTable:
  202.     FOR A = 0 TO 6576
  203.         Prefix(A) = -1
  204.         Suffix(A) = -1
  205.         Code(A) = -1
  206.     NEXT
  207. RETURN
  208.  
  209. 'Reads one byte from the input buffer, and fills the buffer if it's emty.
  210. FUNCTION GetByte STATIC
  211.     IF IAddress = IEndAddress THEN
  212.         GET #1, , InBuffer$
  213.         A& = SADD(InBuffer$)
  214.         A& = A& - 65536 * (A& < 0)
  215.         Iseg = SSEG(InBuffer$) + (A& \ 16)
  216.         IAddress = (A& MOD 16)
  217.         IEndAddress = IAddress + 4000
  218.     END IF
  219.     DEF SEG = Iseg
  220.     GetByte = PEEK(IAddress)
  221.     IAddress = IAddress + 1
  222. END FUNCTION
  223.  
  224. 'Attempts to finds a prefix:suffix string.
  225. SUB Hash (Prefix, Suffix, Index, Found)
  226.     
  227.     Index = (Prefix * 256& XOR Suffix) MOD 6577 'XOR hashing
  228.     IF Index = 0 THEN  'is Index lucky enough to be 0?
  229.         Offset = 1     'Set offset to 1, because 6577-0=6577
  230.     ELSE
  231.         Offset = 6577 - Index
  232.     END IF
  233.     DO 'until we find a match or don't
  234.         IF Code(Index) = -1 THEN      'is there nothing here?
  235.             Found = False             'yup, not found
  236.             EXIT SUB
  237.         'is this entry what we're looking for?
  238.         ELSEIF Prefix(Index) = Prefix AND Suffix(Index) = Suffix THEN
  239.             Found = True              'yup, found
  240.             EXIT SUB
  241.         ELSE 'retry until we find what were looking for or we find a blank
  242.              'entry
  243.             Index = Index - Offset
  244.             IF Index < 0 THEN 'is index too far down?
  245.                 Index = Index + 6577 'yup, bring it up then
  246.             END IF
  247.         END IF
  248.     LOOP
  249. END SUB
  250.  
  251. 'Throws a byte into the output buffer and writes the buffer if it's full.
  252. SUB PutByte (A) STATIC
  253.     IF OAddress = OEndAddress THEN
  254.         PUT #2, , OutBuffer$
  255.         OAddress = OStartAddress
  256.     END IF
  257.     DEF SEG = Oseg
  258.     POKE OAddress, A
  259.     OAddress = OAddress + 1
  260. END SUB
  261.  
  262. 'Throws one multi-bit code to the output file.
  263. SUB PutCode (A) STATIC
  264.     SHARED MaxCode
  265.     IF A >= MaxCode THEN STOP
  266.  
  267.     Char& = Char& + A * Shift(CurrentBit)
  268.     CurrentBit = CurrentBit + CodeSize
  269.     DO WHILE CurrentBit > 7
  270.         PutByte Char& AND 255
  271.         Char& = Char& \ 256
  272.         CurrentBit = CurrentBit - 8
  273.     LOOP
  274. END SUB
  275.  
  276. 'This is the "experimental" part of the program. This procedure eliminates
  277. 'any strings which are not used in the encoding table: the usual result of
  278. 'doing this is greater compression.
  279. 'It isn't documented well yet... I'm still working on it.
  280. SUB Rebuild.Table (New.Entries)
  281.     DIM P(4096), S(4096), U(4096) AS LONG, Pn(4096), C(4096)
  282.     DIM Location(4096)
  283.    
  284.     SHARED StartCode, MaxCode, Prefix
  285.     Num.Entries = 0
  286.     
  287.     FOR A = 0 TO 6576
  288.         C = Code(A)
  289.         IF C <> -1 THEN 'valid code?
  290.             IF Used(C) > 0 THEN 'was it used at all?
  291.                 Used(C) = 0
  292.                 P = Prefix(A): S = Suffix(A)
  293.                 P(Num.Entries) = P          'put it into a temporary table
  294.                 S(Num.Entries) = S
  295.                 U(Num.Entries) = P * 4096& + S
  296.                 C(C) = Num.Entries
  297.                 Num.Entries = Num.Entries + 1
  298.             END IF
  299.         END IF
  300.     NEXT
  301.     
  302.  
  303.     Num.Entries = Num.Entries - 1
  304.     FOR A = 0 TO Num.Entries
  305.         Pn(A) = A
  306.     NEXT
  307.         'sort the table according to it's prefix:suffix
  308.     Mid = Num.Entries \ 2
  309.     DO
  310.         FOR A = 0 TO Num.Entries - Mid
  311.             IF U(Pn(A)) > U(Pn(A + Mid)) THEN
  312.                 SWAP Pn(A), Pn(A + Mid)
  313.                 Swap.Flag = True
  314.                 CompareLow = A - Mid
  315.                 CompareHigh = A
  316.                 DO WHILE CompareLow >= 0
  317.                     IF U(Pn(CompareLow)) > U(Pn(CompareHigh)) THEN
  318.                         SWAP Pn(CompareLow), Pn(CompareHigh)
  319.                         CompareHigh = CompareLow
  320.                         CompareLow = CompareLow - Mid
  321.                     ELSE
  322.                         EXIT DO
  323.                     END IF
  324.                 LOOP
  325.                
  326.             END IF
  327.         NEXT
  328.         
  329.         Mid = Mid \ 2
  330.     LOOP WHILE Mid > 0
  331.     
  332.     
  333.     FOR A = 0 TO Num.Entries
  334.         Location(Pn(A)) = A
  335.     NEXT
  336.     'clear the old hash table
  337.     FOR A = 0 TO 6576
  338.         Prefix(A) = -1
  339.         Suffix(A) = -1
  340.         Code(A) = -1
  341.     NEXT
  342.     
  343.     'put each prefix:suffix into the hash table
  344.     FOR A1 = 0 TO Num.Entries
  345.         A = Pn(A1)
  346.        
  347.         P = P(A)
  348.         S = S(A)
  349.         IF P >= StartCode THEN 'is it pointing twards a string?
  350.             P = StartCode + Location(C(P)) 'yup; update the pointer
  351.         END IF
  352.         IF S >= StartCode THEN
  353.             S = StartCode + Location(C(S))
  354.         END IF
  355.         'where does this prefix:suffix go?
  356.         Hash P, S, Index, 0
  357.         'put it there
  358.         Prefix(Index) = P
  359.         Suffix(Index) = S
  360.         Code(Index) = A1 + StartCode
  361.         
  362.     NEXT
  363.     '# of entries in the hash table now
  364.     New.Entries = Num.Entries + 1
  365. END SUB
  366.  
  367.